Skip to content

Batch authorization graph snapshot in one SQL round trip (TC-735 dependency) - #302

Open
samgbafa wants to merge 4 commits into
mainfrom
skgbafa/tc-735-graph-snapshot-batch
Open

samgbafa wants to merge 4 commits into
mainfrom
skgbafa/tc-735-graph-snapshot-batch

Conversation

@samgbafa

@samgbafa samgbafa commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Change

Fetch authorization closure edges, delegations, abilities and revocations in one tagged SQL query; rebuild the snapshot in Rust. Capability validation, revocation rules and schema stay unchanged. This PR is required before TC-735's pre-persist write-path PR #301.

The metadata joins use a planner-visible 65-node (64 valid + overflow sentinel) lookup relation. The full closure and edge budget checks remain separate; 64 nodes load completely and 65/66 fail closed. Ability rows have a canonical typed (resource, ability) order across databases. Allow/deny outcomes do not change; only the diagnostic reason when several caveats all reject may normalize from the former backend-dependent order. SDK/CLI do not assert that graph rejection text; existing node containment-vector codes test the separate containment helper.

Main integration and TC-1071 alias revocations

Main (a735c4d) made revocation lookups alias-aware (TC-1071): revoking any stored encoding of a delegation revokes every stored encoding with the same canonical identity (revocation::effective_rows). The previous single statement joined revocation.revoked directly, and git merged that SQL without a conflict, so a naive merge would have accepted a delegation revoked under another encoding.

  • 80e74ae merges main. It keeps main's effective_rows call after the snapshot (correct, one extra round trip), so no commit in this branch is alias-blind. Production chain locks use main's load_chain_lock_ids; load_closure_edges is now test-only. This intermediate commit was not tested on its own; its one-round-trip assertion is expected to fail there.
  • 580abe5 adds an alias-joined revocation branch to the same statement (lookup_nodes → delegation_cid_alias source → same-canonical target → revocation.revoked), mirroring effective_rows, and removes the extra query. Signed revocation targets and stored IDs are unchanged.

Evidence

All at 580abe562cf20c06b32a5124e76f0c0502ee30d8 (Sam Gbafa), local PostgreSQL 16.15, MySQL 8.0.46, MinIO; PostgreSQL 18.6 for plans.

  • RED→GREEN alias regression (assert_alias_revocation_refused): chain leaf → mid → root; mid and another stored encoding share a canonical alias; the revocation names the other encoding. revocation::is_revoked(mid) is true. Before 580abe5's statement change the batched snapshot did not mark mid revoked on SQLite, PostgreSQL 16 and MySQL 8 (~/scratch/tc-735/pr302-alias-red.log); after, it reports mid revoked, the leaf's first revoked ancestor is mid, and the snapshot equals the sequential one.
  • Seeded property test (assert_batched_matches_sequential_decisions; 200 SQLite, 60 PostgreSQL and 60 MySQL cases): random DAGs of 1–6 delegations, alias groups including uncited stored encodings, revocations of cited and uncited encodings, and expired delegations. For each single root and the all-roots set it requires: batched snapshot == sequential effective_rows snapshot; per-root is_revoked/first_revoked_ancestor refusal == per-node DB revocation::is_revoked/first_revoked_ancestor; and the production authorize_holder result == the same checks run on the sequential snapshot. It asserts alias-only refusals and both outcomes occurred. Run alone against the alias-blind statement, it fails with the seed in the message (~/scratch/tc-735/pr302-property-red.log).
  • One statement still: snapshot_load_is_one_round_trip_and_matches_sequential passes; wire budget sign-in 69, KV PUT 34, KV GET 16 (unchanged; ~/scratch/tc-735/pr302-580abe5-wire.log).
  • PostgreSQL 16.15 and 18.6 EXPLAIN (ANALYZE, BUFFERS) with 100,000 unrelated delegations, abilities and alias rows and about 50,000 revocations: the alias branch uses delegation_cid_alias_pkey, idx_delegation_cid_alias_canonical and idx_revocation_revoked; no sequential scan of those tables, no temporary spill, 0.195/0.187 ms (~/scratch/tc-735/pr302-alias-plan-pg{16,18}-100k.txt).
  • Full serialized workspace suite: 1,207 passed, 12 ignored, 0 failed (CI unset only for the privileged iptables fixture). Strict all-target Clippy and formatting pass (~/scratch/tc-735/pr302-merge-full-suite.log).
  • The repository's full MySQL migrator still hits an older indexed-column length error; the MySQL auth-graph tests use an isolated table fixture, now including delegation_cid_alias.

Coordination

TC-1149/#298 principal/alias closure helpers are preserved through main. Requesting a fresh security review of the alias delta (80e74ae..580abe5) plus the merge resolution. No merge or deployment requested; TC-1176 release hold applies.

@samgbafa samgbafa left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE — TC-735

@samgbafa

Copy link
Copy Markdown
Contributor Author

REQUEST_CHANGES: see TC-735

@samgbafa samgbafa left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REQUEST_CHANGES — TC-735

@samgbafa

Copy link
Copy Markdown
Contributor Author

APPROVE: see TC-735

@samgbafa samgbafa left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE — TC-735

Keep main's alias-aware revocation::effective_rows after the batched
snapshot (an extra round trip) so this merge alone preserves TC-1071;
the next commit folds it into the single statement. Production locking
now uses main's load_chain_lock_ids; load_closure_edges is a test oracle.
TC-1071 revokes every stored encoding sharing a canonical identity.
Add an alias-joined revocation branch to the single snapshot statement,
mirroring revocation::effective_rows, and drop the merge's extra query.
Regression: a revocation under another encoding is refused on SQLite,
PostgreSQL and MySQL. A seeded property test compares production
batched decisions with the sequential effective_rows snapshot over
random DAGs, alias groups, revoked and expired delegations.
@samgbafa

Copy link
Copy Markdown
Contributor Author

APPROVE: see TC-735 (head 580abe5)

samgbafa added a commit that referenced this pull request Oct 17, 2026
Brings main a735c4d (TC-1071 CID-alias revocations, TC-1176 boot
lock_timeout, request telemetry) and #302 580abe5, whose batched auth
snapshot resolves alias revocations in its one statement.

Startup keeps the pre-S3-listing migration (now main's lock_timeout-
bounded migrate) so deleting fences are read before the quota listing,
then opens the database through main's new_with_encryption, which finds
migrations current and runs the CID-alias backfill with the column key
before serving requests.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant